use aspect_core::message_queue::MessageQueueId;
pub use jsonrpsee::core::{async_trait, RpcResult};
use jsonrpsee::proc_macros::rpc;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

pub use aspect_ident::DeviceIdent;
pub use aspect_rpc_common::HexBuf;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserRef(pub String);

#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Status {
    /// Not active, nothing to do.
    Idle,

    /// Normal operation.
    Running,

    /// When unable to connect to all peers or something?
    Degraded,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct StatusReport {
    /// Serialized device ID.
    pub device_id: String,

    /// Self user reference.
    pub self_ref: UserRef,

    /// Client status.
    pub status: Status,

    /// Connected peers.
    pub peers: Vec<PeerState>,
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PeerRelationship {
    Homeserver,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct PeerState {
    /// Peer connection string.
    pub peer_str: String,

    /// Our relationship with the peer.
    pub rel: PeerRelationship,

    /// If currently connected.
    pub active: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChannelRef(pub String);

/// Hex-encoded codec-encoded message
#[serde_as]
#[derive(Clone, Serialize, Deserialize)]
pub struct MsgPayload(#[serde_as(as = "serde_with::hex::Hex")] pub Vec<u8>);

#[rpc(server, namespace = "aspc")]
pub trait ClientRpc {
    #[method(name = "status")]
    async fn status(&self) -> RpcResult<StatusReport>;

    #[method(name = "stop")]
    async fn stop(&self) -> RpcResult<()>;

    #[method(name = "add_peer")]
    async fn add_peer(&self, peer_str: String) -> RpcResult<()>;

    /// Registers a new account at a homeserver.
    #[method(name = "register")]
    async fn register(
        &self,
        homeserver: String,
        username: String,
        password: String,
        extra_cred: Option<Vec<u8>>,
    ) -> RpcResult<()>;

    /// Logs into an existing account at a homeserver.
    #[method(name = "login")]
    async fn login(&self, homeserver: String, username: String, password: String) -> RpcResult<()>;

    /// Gets an encoded DIR package by its ident hex string.
    #[method(name = "get_device_data")]
    async fn get_device_data(&self, devident: String) -> RpcResult<HexBuf>;

    /// Gets device idents for a user query string.
    #[method(name = "get_hs_user_devices")]
    async fn get_hs_user_devices(&self, user_query: String) -> RpcResult<Vec<String>>;

    #[method(name = "create_channel")]
    async fn create_channel(&self, init_users: Vec<UserRef>) -> RpcResult<ChannelRef>;

    #[method(name = "send_channel_msg")]
    async fn send_channel_msg(&self, ch: ChannelRef, msg_payload: String) -> RpcResult<()>;

    // TODO make this use typed message queue names
    #[method(name = "list_device_queues")]
    async fn list_device_queues(&self) -> RpcResult<Vec<String>>;

    #[method(name = "get_server_messages")]
    async fn get_server_notifs(&self) -> RpcResult<Vec<String>>;

    #[method(name = "send_raw_d2d_msg")]
    async fn send_raw_d2d_msg(&self, target_device: String, payload_hex: String) -> RpcResult<()>;
}
